package com.noble.glamour.module.logic.socket.logic;

import com.noble.glamour.module.logic.socket.NetData;
import com.noble.glamour.module.logic.socket.TCPConstant;
import com.noble.glamour.module.logic.socket.listener.SocketConnectionListener;
import com.noble.glamour.module.logic.socket.stream.PackerReader;
import com.noble.glamour.module.logic.socket.stream.PacketWriter;
import com.noble.glamour.module.logic.socket.utils.IMUtils;
import com.noble.glamour.module.logic.socket.utils.SocketState;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

import javax.net.SocketFactory;

/**
 * socket通信内核： 封装了连接服务器、接收、发送数据等功能。
 */
public class KeepAliveSocket {
    private Socket socket;
    private volatile SocketState state = SocketState.INVALID;

    private PacketWriter packetWriter;
    private PackerReader packerReader;

    private boolean wantClose = false;            // socket是否正常关闭
    private SocketConnectionListener listener;    // socket连接状态监听

    // socket 连接状态
    private void setState(SocketState newState) {
        if (state == newState || newState == null) {
            return;
        }
        state = newState;

        switch (newState) {
            case CONNECTING:
                IMUtils.log("Socket connecting");
                if (listener != null) {
                    listener.onSocketConnecting();
                }
                break;
            case CONNECTED_SUCCESS:
                IMUtils.log("Socket connect success");
                if (listener != null) {
                    listener.onSocketConnected();
                }
                break;
            case CONNECTED_FAILED:
                IMUtils.log("Socket connect fail");
                if (listener != null) {
                    listener.onSocketConnectError();
                }
                break;
            case DISCONNECTING:
                IMUtils.log("Socket disconnecting");
                break;
            case DISCONNECT_ERROR:
                IMUtils.log("Socket disconnect error");
                if (listener != null) {
                    listener.onSocketDisconnectByError();
                }
                break;
            case DISCONNECT_NORMAL:
                IMUtils.log("Socket disconnect normal");
                if (listener != null) {
                    listener.onSocketDisconnectNormally();
                }
                break;
            default:
                break;
        }
    }

    public void setSocketStateListener(SocketConnectionListener listener) {
        this.listener = listener;
    }

    public void connect() {
        if (state == SocketState.CONNECTING || state == SocketState.CONNECTED_SUCCESS) {
            IMUtils.log("Socket already connected");
            return;
        }

        IMUtils.log("connect: ------>socket开始连接，hostIP：" + TCPConstant.getTcpHost() + ":" + TCPConstant.getTcpPort());
        setState(SocketState.CONNECTING);

        OutputStream output;
        InputStream input;
        try {
            SocketAddress address = new InetSocketAddress(TCPConstant.getTcpHost(), TCPConstant.getTcpPort());
            socket = SocketFactory.getDefault().createSocket();
            socket.connect(address, TCPConstant.SOCKET_CONNECT_TIMEOUT);
            input = socket.getInputStream();
            output = socket.getOutputStream();

        } catch (IOException e) {
            e.printStackTrace();
            setState(SocketState.CONNECTED_FAILED);
            return;
        }

        if (packerReader == null) {
            packerReader = new PackerReader(listener);
            packerReader.init(this, input);
        }

        if (packetWriter == null) {
            packetWriter = new PacketWriter(state, listener);
            packetWriter.init(this, output);
        }

        setState(SocketState.CONNECTED_SUCCESS);
    }

    public void sendPacket(NetData data) {
        if (packetWriter == null) {
            IMUtils.log("not connected or writer not ready?");
            if (listener != null) {
                listener.onSendPacketError(state, data);
            }
            return;
        }
        IMUtils.log("------发数据包: " + data.getMsgBody());
        packetWriter.sendPacket(data);
    }

    /**
     * @param instant true正常关闭，false非正常关闭
     */
    public void disconnect(boolean instant) {
        wantClose = instant;
        if (state != SocketState.CONNECTING && state != SocketState.CONNECTED_SUCCESS) {
            return;
        }
        setState(SocketState.DISCONNECTING);
        IMUtils.log("Socket disconnect by instant " + instant + " start");
        if (packetWriter != null) {
            packetWriter.shutdown(instant);
        }

        if (packerReader != null) {
            packerReader.shutdown(instant);
        }
    }

    public void onReadClose() {
        IMUtils.log("Socket reader close");
        packerReader = null;
        check2Close();
    }

    public void onWriterClose() {
        IMUtils.log("Socket writer close");
        packetWriter = null;
        check2Close();
    }

    public void onIOError() {
        disconnect(false);
    }

    public SocketState getSocketState() {
        return state;
    }

    private void check2Close() {
        if (packerReader != null) {
            return;
        }
        if (packetWriter != null) {
            return;
        }

        IMUtils.log("Socket close");
        try {
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (wantClose) {
            setState(SocketState.DISCONNECT_NORMAL);
        } else {
            setState(SocketState.DISCONNECT_ERROR);
        }
    }
}
